package com.gitee.wsl.compose.chart.sample

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.absolutePadding
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.selection.selectable
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PathEffect
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.unit.dp
import com.gitee.wsl.common.ui.draw.style.LineStyle
import com.gitee.wsl.compose.chart.ChartLayout
import com.gitee.wsl.compose.chart.ExperimentalKoalaPlotApi
import com.gitee.wsl.compose.chart.KoalaPlotTheme
import com.gitee.wsl.compose.chart.SelectChangedListener
import com.gitee.wsl.compose.chart.SelectCheck

import com.gitee.wsl.compose.chart.generateHueColorPalette
import com.gitee.wsl.compose.chart.legend.Flow
import com.gitee.wsl.compose.chart.legend.Legend
import com.gitee.wsl.compose.chart.legend.LegendLocation
import com.gitee.wsl.compose.chart.xy.XYGraph
import com.gitee.wsl.compose.chart.xy.axis.model.CategoryAxisModel
import com.gitee.wsl.compose.chart.xy.axis.model.LongLinearAxisModel
import com.gitee.wsl.compose.chart.xy.axis.style.rememberAxisStyle
import com.gitee.wsl.compose.chart.xy.bar.DefaultBar
import com.gitee.wsl.compose.chart.xy.bar.stack.StackedVerticalBarPlot
import com.gitee.wsl.compose.chart.xy.bar.stack.model.BarStackedEntry

import com.gitee.wsl.compose.modifier.layout.VerticalRotation
import com.gitee.wsl.compose.modifier.layout.rotateVertically
import com.gitee.wsl.compose.ui.widget.Symbol
import com.gitee.wsl.compose.modifier.layout
import com.gitee.wsl.ext.number.toString


private val colors = generateHueColorPalette(PopulationData.Categories.entries.size)

private const val BarWidth = 0.8f

private val rotationOptions = listOf(0, 30, 45, 60, 90)

private fun barChartEntries(): List<BarStackedEntry<Int, Long>> {

    return PopulationData.years.mapIndexed { yearIndex, year ->
        object : BarStackedEntry<Int, Long> {
            override val x: Int = year
            override val yOrigin: Long = 0L

            override val y: List<Long> = object : AbstractList<Long>() {
                override val size: Int
                    get() = PopulationData.Categories.entries.size

                override fun get(index: Int): Long {
                    return PopulationData.Categories.entries.subList(0, index + 1).fold(0L) { acc, cat ->
                        acc + PopulationData.data[cat]!![yearIndex]
                    }
                }
            }
        }
    }
}

@OptIn(ExperimentalKoalaPlotApi::class)
@Composable
private fun LegendView(thumbnail: Boolean = false,selectCheck: SelectCheck,selectChangedListener: SelectChangedListener?) {
    if (!thumbnail) {
        Surface(shadowElevation = 2.dp) {
            Legend.Flow(
                itemCount = PopulationData.Categories.entries.size,
                symbol = { i,selected ->
                    Symbol(modifier = Modifier.size(padding), fillBrush = SolidColor(colors[i]))
                },
                label = { i,selected,listener ->
                    Text(PopulationData.Categories.entries[i].toString())
                },
                modifier = paddingMod,
                selectCheck = selectCheck,
                selectChangedListener = selectChangedListener
            )
        }
    }
}

val stackedVerticalBarSampleView = object : SampleView {
    override val name: String = "Stacked Vertical Bar"

    override val thumbnail = @Composable {
        ThumbnailTheme {
            StackedBarSamplePlot(true, name)
        }
    }

    override val content: @Composable () -> Unit = @Composable {
        var selectedOption by remember { mutableStateOf(rotationOptions[0]) }
        KoalaPlotTheme(axisThemeStyle = KoalaPlotTheme.axisThemeStyle.copy(minorGridlineStyle = minorGridLineStyle)) {
            Column {
                StackedBarSamplePlot(
                    false,
                    "New York City Population",
                    Modifier.weight(1f),
                    selectedOption
                )
                ExpandableCard(
                    colors = CardDefaults.elevatedCardColors(),
                    elevation = CardDefaults.elevatedCardElevation(),
                    titleContent = {
                        Text("X-Axis Label Angle", modifier = paddingMod)
                    }
                ) {
                    Column {
                        rotationOptions.forEach {
                            Row(
                                Modifier.fillMaxWidth()
                                    .selectable(selected = (it == selectedOption), onClick = { selectedOption = it })
                            ) {
                                RadioButton(selected = (it == selectedOption), onClick = { selectedOption = it })
                                Text(text = it.toString())
                            }
                        }
                    }
                }
            }
        }
    }
}

private const val PopulationScale = 1E6

@OptIn(ExperimentalKoalaPlotApi::class)
@Composable
private fun StackedBarSamplePlot(
    thumbnail: Boolean = false,
    title: String,
    modifier: Modifier = Modifier,
    xAxisLabelRotation: Int = 0
) {
    val barChartEntries = remember { barChartEntries() }

    ChartLayout(
        modifier = modifier.then(paddingMod),
        title = { ChartTitle(title) },
        legend = {selected,listener-> LegendView(thumbnail,selected,listener) },
        legendLocation = LegendLocation.BOTTOM
    ) {
        @Suppress("MagicNumber")
        XYGraph(
            xAxisModel = CategoryAxisModel(PopulationData.years),
            yAxisModel = LongLinearAxisModel(0L..10000000, minimumMajorTickIncrement = 1000000),
            xAxisStyle = rememberAxisStyle(labelRotation = xAxisLabelRotation),
            xAxisLabels = {
                if (!thumbnail) AxisLabel("$it", Modifier.padding(top = 2.dp))
            },
            xAxisTitle = {
                if (!thumbnail) AxisTitle("Year", modifier = paddingMod)
            },
            yAxisStyle = rememberAxisStyle(minorTickSize = 0.dp),
            yAxisLabels = {
                if (!thumbnail) {
                    AxisLabel(
                        (it / PopulationScale).toString(2),
                        Modifier.absolutePadding(right = 2.dp)
                    )
                }
            },
            yAxisTitle = {
                if (!thumbnail) {
                    AxisTitle(
                        "Population (Millions)",
                        modifier = Modifier
                            .layout {
                                rotateVertically(VerticalRotation.COUNTER_CLOCKWISE)
                            }
                            .padding(bottom = padding)
                    )
                }
            },
            verticalMajorGridLineStyle = null
        ) {
            StackedVerticalBarPlot(
                barChartEntries,
                barWidth = BarWidth,
                bar = { xIndex, barIndex ->
                    DefaultBar(
                        brush = SolidColor(colors[barIndex]),
                        modifier = Modifier.fillMaxWidth()
                    ) {
                        if (!thumbnail) {
                            HoverSurface {
                                val borough = PopulationData.Categories.entries[barIndex]
                                val pop = PopulationData.data[borough]!![xIndex]
                                Text("$borough: $pop")
                            }
                        }
                    }
                }
            )
        }
    }
}

private val minorGridLineStyle = LineStyle(
    brush = SolidColor(Color.LightGray),
    pathEffect = PathEffect.dashPathEffect(floatArrayOf(2f, 2f))
)
